N2O over MQTT + KVS https://n2o.space Пришла пора завернуть весь стек протоколов N2O в какой транспорт, который поддерживает, что-то более сложное чем ACK, такой транспорт — MQTT, он легковеснее AMQP, который, давайте без обид, порос кольцами уже, как и riak. Вообщем, пока emqtt.io еще позволяет, нужно быстро форкануться и повыбрасывать оттуда половину либ. Из чего состоит EMQ — компания производитель EMQ Enterprise, Inc, автор Feng Lee. В основе лежит esockd — очередной неблокирующий сервер (типа ranch), в качестве веб сервера используется mochiweb, его же и вебсокеты. emqtt.io — это пример как нужно писать приложения, минимальная документация, отпидерашеный код, приятно работать. Только там все на lager завязали и в итоге, чтобы залогировать нужно lager, lager_syslog, gen_logger, goldrush, syslog. Поэтому пришлось форкануть это все. Или просто выброосить, но lager в продакшине любят, так как проверен. Ладно не буду пока удалять. Теперь о рекомендациях по общей архитектуре. Стек протоколов N2O я вижу как федерацию, аналогичную CORBA/OASIS/XMPP поверх различных транспортов. До этого — это был BERT/JSON кодировка over WS/WSS транспорт, теперь качество и количество транспортов расширилось до MQTT over WS/WSS, TCP/TLS. Т.е. появился базовый посредник, с ack+pubsub+presence+ping протоколом, соответственно N2O HEART ["PING","N2O,"] протокол уже не нужен. Такая версия N2O расширит области применения. С другой стороны дополнительное кодирование MQTT и его заголовки будут потреблять больше ресурсов. Официальный MQTT JavaScript клиент от IBM: https://github.com/voxoz/mq/blob/master/apps/emq_dashboard/priv/www/assets/js/mq ttws31.js Будем его резать и интегрировать в N2O. Есть альтернативный mqtt.js — но там как-то слишком адово как по нашим меркам. Для джаваскриптеров такое в порядке вещей считается. MQTT как сервер приложений. Ну какие могут быть приложения у MQTT — коннектить устройства IoT. Чтобы реализовать перситенс — нужно имплементировать публичные хуки MQTT протокола: load(Env) -> emqttd:hook('client.connected', fun ?MODULE:on_connected/3, [Env]), emqttd:hook('client.disconnected', fun ?MODULE:on_disconnected/3, [Env]), emqttd:hook('client.subscribe', fun ?MODULE:on_subscribe/3, [Env]), emqttd:hook('client.subscribe.after', fun ?MODULE:on_subscribe_after/3, [Env]), emqttd:hook('client.unsubscribe', fun ?MODULE:on_unsubscribe/3, [Env]), emqttd:hook('message.publish', fun ?MODULE:on_publish/2, [Env]), emqttd:hook('message.delivered', fun ?MODULE:on_delivered/3, [Env]), emqttd:hook('message.acked', fun ?MODULE:on_acked/3, [Env]). Вообщем-то это все, что нужно знать людям, которые принимают решения. Инструкция для mad хипстеров не признающих rebar, mix и hex: $ brew install erlang $ curl -fsSL https://raw.github.com/synrc/mad/master/mad > mad \ && chmod +x mad \ && sudo cp /usr/local/bin $ git clone git://github.com/voxoz/mq && cd mq $ mad dep com rep Configuration: [{emq_dashboard, [{listeners_dash, [{http,18083,[{acceptors,4},{max_clients,512}]}]}]}, {emqttd, [{listeners, [{http,8083,[{acceptors,4},{max_clients,512}]}]}, {sysmon, [{long_gc,false}, {long_schedule,240}, {large_heap,8000000}, {busy_port,false}, {busy_dist_port,true}]}, {session, [{upgrade_qos,off}, {max_inflight,32}, {retry_interval,20}, {max_awaiting_rel,100}, {await_rel_timeout,20}, {enable_stats,off}]}, {queue,[]}, {allow_anonymous,true}, {protocol, [{max_clientid_len,1024},{max_packet_size,64000}]}, {acl_file,"etc/acl.conf"}, {plugins_etc_dir,"etc/plugins/"}, {plugins_loaded_file,"etc/loaded_plugins"}, {pubsub, [{pool_size,8},{by_clientid,true},{async,true}]}]}, {emq_modules, [{modules, [{emq_mod_presence,[{qos,1}]}, {emq_mod_subscription,[{<<"%u/%c/#">>,2}]}, {emq_mod_rewrite, [{rewrite,"x/#","^x/y/(.+)$","z/y/$1"}, {rewrite,"y/+/z/#","^y/(.+)/z/(.+)$", "y/z/$2"}]}]}]}, {kvs, [{dba,store_mnesia}, {schema,[kvs_user,kvs_acl,kvs_feed,kvs_subscription]}]}, {n2o,[]}] Applications: [kernel,stdlib,gproc,lager_syslog,pbkdf2,asn1,gen_logger, mnesia,compiler,inets,crypto,syntax_tools,xmerl,kvs,esockd, goldrush,public_key,lager,ssl,mochiweb,emqttd,mad,sh,syslog] Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] Eshell V8.2 (abort with ^G) 1> 19:17:23.670 [info] Application lager started on node nonode@nohost 19:17:23.746 [info] Application ssl started on node nonode@nohost 19:17:23.749 [info] Application mochiweb started on node nonode@nohost starting emqttd on node 'nonode@nohost' Nonexistent: [] emqttd ctl is starting...[ok] emqttd hook is starting...[ok] emqttd router is starting...[ok] emqttd pubsub is starting...[ok] emqttd stats is starting...[ok] emqttd metrics is starting...[ok] emqttd pooler is starting...[ok] emqttd trace is starting...[ok] emqttd client manager is starting...[ok] emqttd session manager is starting...[ok] emqttd session supervisor is starting...[ok] emqttd wsclient supervisor is starting...[ok] emqttd broker is starting...[ok] emqttd alarm is starting...[ok] emqttd mod supervisor is starting...[ok] emqttd bridge supervisor is starting...[ok] emqttd access control is starting...[ok] emqttd system monitor is starting...[ok] Plugins: [{mqtt_plugin,emq_auth_username,"2.1.1", "Authentication with Username/Password",false}, {mqtt_plugin,emq_dashboard,"2.1.1","EMQ Web Dashboard",false}, {mqtt_plugin,emq_modules,"2.1.1","EMQ Modules",false}, {mqtt_plugin,emq_persistence,"1.1.2","Synrc KVS for MQTT",false}] Names: [emq_dashboard,emq_modules,emq_persistence,emq_auth_username] dashboard:http listen on 0.0.0.0:18083 with 4 acceptors. 19:17:25.705 [info] started Apps: [emq_dashboard] 19:17:25.705 [info] load plugin emq_dashboard successfully 19:17:25.705 [info] Application emq_dashboard started on node nonode@nohost Load emq_mod_presence module successfully. Load emq_mod_subscription module successfully. Load emq_mod_rewrite module successfully. 19:17:25.802 [info] started Apps: [emq_modules] 19:17:25.802 [info] load plugin emq_modules successfully 19:17:25.802 [info] Application emq_modules started on node nonode@nohost 19:17:25.848 [info] started Apps: [emq_persistence] 19:17:25.848 [info] load plugin emq_persistence successfully 19:17:25.849 [info] Application emq_persistence started on node nonode@nohost 19:17:25.895 [warning] CLI: users is overidden by {emq_auth_username,cli} 19:17:25.895 [info] started Apps: [emq_auth_username] 19:17:25.895 [info] load plugin emq_auth_username successfully 19:17:25.895 [info] Application emq_auth_username started on node nonode@nohost mqtt:ws listen on 0.0.0.0:8083 with 4 acceptors. emqttd 2.1.1 is running now 19:17:25.897 [info] Application emqttd started on node nonode@nohost 19:17:25.904 [info] Application mad started on node nonode@nohost 19:17:25.957 [info] Application sh started on node nonode@nohost 19:17:26.043 [info] Application syslog started on node nonode@nohost Open http://127.0.0.1:18083/#/websocket with admin:public credentials, Press Connect, Subscribe, Send and observe statistics http://127.0.0.1:18083/#/overview.